package com.ruiyou.ub;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

public class MovableFAB extends FloatingActionButton {
    public MovableFAB(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldW, int oldH) {
        super.onSizeChanged(w, h, oldW, oldH);
        if (w > 0 && h > 0) {
            this.w = w;
            this.h = h;
            pw = ((View) getParent()).getWidth();
            ph = ((View) getParent()).getHeight();
            layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
        }
    }

    public void resizeParent() {
        ((View) getParent()).addOnLayoutChangeListener(
                new View.OnLayoutChangeListener() {
                    @Override
                    public void onLayoutChange(
                            View v, int left, int top, int right, int bottom,
                            int oldLeft, int oldTop, int oldRight, int oldBottom
                    ) {
                        pw = v.getWidth();
                        ph = v.getHeight();
                        if (pw > 0 && ph > 0) {
                            setX(pw - w - layoutParams.rightMargin);
                            setY(ph - h - layoutParams.bottomMargin);
                        }
                        removeOnLayoutChangeListener(this);
                    }
                }
        );
    }

    private void moveToEdge() {
        float x = getX(), y = getY(); // destination
        // move  to edge
        float edgeX = Math.min(
                x - layoutParams.leftMargin,
                pw - layoutParams.rightMargin - x - w
        );
        float edgeY = Math.min(
                y - layoutParams.topMargin,
                ph - layoutParams.bottomMargin - y - h
        );
        if (edgeX > edgeY) { // anchor to y
            if (y - layoutParams.topMargin >
                    ph - layoutParams.bottomMargin - y - h) {
                y = ph - layoutParams.bottomMargin - h;
            } else {
                y = layoutParams.topMargin;
            }
        } else { // anchor to x
            if (x - layoutParams.leftMargin >
                    pw - layoutParams.rightMargin - x - w) {
                x = pw - layoutParams.rightMargin - w;
            } else {
                x = layoutParams.leftMargin;
            }
        }
        animate().x(x).y(y).start();
    }

    private static final int DRAG_TOLERANCE = 10;
    private float downRawX, downRawY, downX, downY, w, h, pw, ph;
    private ViewGroup.MarginLayoutParams layoutParams;

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(@NonNull MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downRawX = ev.getRawX();
                downRawY = ev.getRawY();
                downX = ev.getX();
                downY = ev.getY();
                return true;
            case MotionEvent.ACTION_MOVE:
                float newX = ev.getRawX() - downX;
                newX = Math.max(layoutParams.leftMargin, newX);
                newX = Math.min(pw - w - layoutParams.rightMargin, newX);
                float newY = ev.getRawY() - downY;
                newY = Math.max(layoutParams.topMargin, newY);
                newY = Math.min(ph - h - layoutParams.bottomMargin, newY);
                animate().cancel();
                animate().y(newY).x(newX).setDuration(0).start();
                return true;
            case MotionEvent.ACTION_UP:
                float upRawY = ev.getRawY();
                float upRawX = ev.getRawX();
                float upDX = Math.abs(upRawX - downRawX);
                float upDY = Math.abs(upRawY - downRawY);
                moveToEdge();
                if (upDX < DRAG_TOLERANCE && upDY < DRAG_TOLERANCE) {
                    return performClick();
                } else {// have drag, so consumed it.
                    return true;
                }
            default:
                break;
        }
        return super.onTouchEvent(ev);
    }
}
